#include <stdio.h>

#include "Data/Serialization.h"
#include "Data/Exceptions.h"

using namespace cmn;

namespace data {

	Scene DeserializeScene( const Json::Value& value ) {
		Scene scene(
			value["name"].asString(),
			value["description"].asString(),
			value["map"].asString(),
			DeserializeCamera( value["camera"] ) );

		Json::Value jsonCreatures = value["creatures"];
		for( size_t i = 0; i < jsonCreatures.size(); ++i )
			scene.AddCreature( DeserializeCreature( jsonCreatures[i] ) );

		Json::Value jsonPlayers = value["players"];
		for( size_t i = 0; i < jsonPlayers.size(); ++i )
			scene.AddPlayer( DeserializePlayer( jsonPlayers[i] ) );

		return scene;
	}

	Json::Value SerializeScene( const Scene& scene ) {
		Json::Value jsonCreatures;
		for( IteratorPtr<Creature> creaturesIter( scene.GetCreatures() ); !creaturesIter.IsEnd(); creaturesIter.Next() )
			jsonCreatures.append( SerializeCreature( creaturesIter.Current() ) );

		Json::Value jsonPlayers;
		for( IteratorPtr<Player> playersIter( scene.GetPlayers() ); !playersIter.IsEnd(); playersIter.Next() )
			jsonPlayers.append( SerializePlayer( playersIter.Current() ) );

		Json::Value value;
		value["name"] = scene.GetName();
		value["description"] = scene.GetDescription();
		value["players"] = jsonPlayers;
		value["creatures"] = jsonCreatures;
		value["map"] = scene.GetMapFileName();
		value["camera"] = SerializeCamera( scene.GetCamera() );
		return value;
	}

	Camera DeserializeCamera( const Json::Value& value ) {
		Transform position = DeserializeTransform( value["position"] );
		Transform velocity = DeserializeTransform( value["velocity"] );
		return Camera( position, velocity );
	}

	Json::Value SerializeCamera( const Camera& camera ) {
		Json::Value value;
		value["position"] = SerializeTransform( camera.GetPosition() );
		value["velocity"] = SerializeTransform( camera.GetVelocity() );
		return value;
	}

	Map DeserializeMap( const Json::Value& value ) {
		Map map(
			value["name"].asString(),
			value["description"].asString() );

		Json::Value jsonObstacles = value["obstacles"];
		for( size_t i = 0; i < jsonObstacles.size(); ++i )
			map.AddObstacle( DeserializeObstacle( jsonObstacles[i] ) );

		return map;
	}

	Json::Value SerializeMap( const Map& map ) {
		Json::Value jsonObstacles;
		for( IteratorPtr<Obstacle> obstaclesIter( map.GetObstacles() ); !obstaclesIter.IsEnd(); obstaclesIter.Next() )
			jsonObstacles.append( SerializeObstacle( obstaclesIter.Current() ) );

		Json::Value value;
		value["name"] = map.GetName();
		value["description"] = map.GetDescription();
		value["obstacles"] = jsonObstacles;
		return value;
	}

	Obstacle DeserializeObstacle( const Json::Value& value ) {
		return Obstacle(
				   value["id"].asString(),
				   DeserializeTransform( value["position"] ),
				   value["model"].asString() );
	}

	Json::Value SerializeObstacle( const Obstacle& obstacle ) {
		Json::Value jsonObstacle;
		jsonObstacle["id"] = obstacle.GetId();
		jsonObstacle["position"] = SerializeTransform( obstacle.GetPosition() );
		jsonObstacle["model"] = obstacle.GetModelFileName();
		return jsonObstacle;
	}

	Player DeserializePlayer( const Json::Value& value ) {
		return Player(
				   DeserializePlayerType( value["type"] ),
				   value["name"].asString(),
				   value["creature"].asString()
			   );
	}

	Json::Value SerializePlayer( const Player& player ) {
		Json::Value value;
		value["type"] = SerializePlayerType( player.GetType() );
		value["name"] = player.GetName();
		value["creature"] = player.GetCreatureId();
		return value;
	}

	PlayerType DeserializePlayerType( const Json::Value& value ) {
		return StringToPlayerType( value.asString() );
	}

	Json::Value SerializePlayerType( const PlayerType& playerType ) {
		return Json::Value( PlayerTypeToString( playerType ) );
	}

	Creature DeserializeCreature( const Json::Value& value ) {
		return Creature(
				   value["id"].asString(),
				   DeserializeTransform( value["position"] ),
				   value["definition"].asString() );
	}

	Json::Value SerializeCreature( const Creature& creature ) {
		Json::Value value;
		value["id"] = creature.GetId();
		value["position"] = SerializeTransform( creature.GetPosition() );
		value["definition"] = creature.GetDefinitionFileName();
		return value;
	}

	CreatureDef DeserializeCreatureDef( const Json::Value& value ) {
		CreatureDef creatureDef(
			value["name"].asString(),
			value["description"].asString(),
			value["head"].asString() );

		Json::Value jsonDetails = value["details"];
		for( size_t i = 0; i < jsonDetails.size(); ++i )
			creatureDef.AddDetail( DeserializeDetail( jsonDetails[i] ) );

		Json::Value jsonRotors = value["rotors"];
		for( size_t i = 0; i < jsonRotors.size(); ++i )
			creatureDef.AddRotor( DeserializeRotor( jsonRotors[i] ) );

		Json::Value jsonLinks = value["links"];
		for( size_t i = 0; i < jsonLinks.size(); ++i )
			creatureDef.AddLink( DeserializeLink( jsonLinks[i] ) );

		return creatureDef;
	}

	Json::Value SerializeCreatureDef( const CreatureDef& creatureDef ) {
		Json::Value jsonDetails;
		for( IteratorPtr<Detail> detailsIter( creatureDef.GetDetails() ); !detailsIter.IsEnd(); detailsIter.Next() )
			jsonDetails.append( SerializeDetail( detailsIter.Current() ) );

		Json::Value jsonRotors;
		for( IteratorPtr<Rotor> rotorsIter( creatureDef.GetRotors() ); !rotorsIter.IsEnd(); rotorsIter.Next() )
			jsonRotors.append( SerializeRotor( rotorsIter.Current() ) );

		Json::Value jsonLinks;
		for( IteratorPtr<Link> linksIter( creatureDef.GetLinks() ); !linksIter.IsEnd(); linksIter.Next() )
			jsonLinks.append( SerializeLink( linksIter.Current() ) );

		Json::Value value;
		value["name"] = creatureDef.GetName();
		value["description"] = creatureDef.GetDescription();
		value["head"] = creatureDef.GetHeadId();
		value["details"] = jsonDetails;
		value["rotors"] = jsonRotors;
		value["links"] = jsonLinks;
		return value;
	}

	Detail DeserializeDetail( const Json::Value& value ) {
		Detail detail(
			value["id"].asString(),
			DeserializeTransform( value["position"] ),
			value["model"].asString() );

		Json::Value jsonRules = value["rules"];
		for( size_t i = 0; i < jsonRules.size(); ++i )
			detail.AddRule( DeserializeRule( jsonRules[i] ) );

		return detail;
	}

	Json::Value SerializeDetail( const Detail& detail ) {
		Json::Value jsonRules;
		for( IteratorPtr<Rule> rulesIter( detail.GetRules() ); !rulesIter.IsEnd(); rulesIter.Next() )
			jsonRules.append( SerializeRule( rulesIter.Current() ) );

		Json::Value value;
		value["id"] = detail.GetId();
		value["position"] = SerializeTransform( detail.GetPosition() );
		value["model"] = detail.GetModelFileName();
		value["rules"] = jsonRules;
		return value;
	}

	Rotor DeserializeRotor( const Json::Value& value ) {
		Rotor rotor(
			value["id"].asString(),
			value["detailA"].asString(),
			value["detailB"].asString(),
			DeserializeVec2( value["position"] ),
			value["definition"].asString()
		);

		Json::Value jsonRules = value["rules"];
		for( size_t i = 0; i < jsonRules.size(); ++i )
			rotor.AddRule( DeserializeRule( jsonRules[i] ) );

		return rotor;
	}

	Json::Value SerializeRotor( const Rotor& rotor ) {
		Json::Value jsonRules;
		for( IteratorPtr<Rule> rulesIter( rotor.GetRules() ); !rulesIter.IsEnd(); rulesIter.Next() )
			jsonRules.append( SerializeRule( rulesIter.Current() ) );

		Json::Value value;
		value["id"] = rotor.GetId();
		value["detailA"] = rotor.GetDetailAId();
		value["detailB"] = rotor.GetDetailBId();
		value["position"] = SerializeVec2( rotor.GetPosition() );
		value["definition"] = rotor.GetDefinitionFileName();
		value["rules"] = jsonRules;

		return value;
	}

	RotorDef DeserializeRotorDef( const Json::Value& value ) {
		return RotorDef(
				   value["name"].asString(),
				   value["description"].asString(),
				   value["torque"].asFloat()
			   );
	}

	Json::Value SerializeRotorDef( const RotorDef& rotorDef ) {
		Json::Value value;
		value["name"] = rotorDef.GetName();
		value["description"] = rotorDef.GetDescription();
		value["torque"] = rotorDef.GetTorque();
		return value;
	}

	Link DeserializeLink( const Json::Value& value ) {
		return Link( value["partA"].asString(), value["partB"].asString() );
	}

	Json::Value SerializeLink( const Link& link ) {
		Json::Value value;
		value["partA"] = link.GetPartAId();
		value["partB"] = link.GetPartBId();
		return value;
	}

	Rule DeserializeRule( const Json::Value& value ) {
		return
			Rule(
				DeserializeCommand( value["command"] ),
				DeserializeAction( value["action"] )
			);
	}

	Json::Value SerializeRule( const Rule& rule ) {
		Json::Value value;
		value["command"] = SerializeCommand( rule.GetCommand() );
		value["action"] = SerializeAction( rule.GetAction() );
		return value;
	}

	Command DeserializeCommand( const Json::Value& value ) {
		return Command(
				   value["key"].asString()[0],
				   DeserializeCommandType( value["type"].asString() )
			   );
	}

	Json::Value SerializeCommand( const Command& command ) {
		Json::Value value;
		value["key"] = ( char ) command.GetKey();
		value["type"] = SerializeCommandType( command.GetType() );
		return value;
	}

	CommandType DeserializeCommandType( const Json::Value& value ) {
		return StringToCommandType( value.asString() );
	}

	Json::Value SerializeCommandType( const CommandType& commandType ) {
		return Json::Value( CommandTypeToString( commandType ) );
	}

	Action DeserializeAction( const Json::Value& value ) {
		return StringToAction( value.asString() );
	}

	Json::Value SerializeAction( const Action& action ) {
		return Json::Value( ActionToString( action ) );
	}

	Model DeserializeModel( const Json::Value& value ) {
		return Model(
				   DeserializeTexture( value["texture"] ),
				   value["body"].asString()
			   );
	}

	Json::Value SerializeModel( const Model& model ) {
		Json::Value jsonValue;
		jsonValue["body"] = model.GetBodyFileName();
		jsonValue["texture"] = SerializeTexture( model.GetTexture() );
		return jsonValue;
	}

	Texture DeserializeTexture( const Json::Value& value ) {
		return Texture(
				   value["file"].asString(),
				   DeserializeTransform( value["position"] ),
				   value["width"].asFloat(),
				   value["height"].asFloat()
			   );
	}

	Json::Value SerializeTexture( const Texture& texture ) {
		Json::Value value;
		value["file"] = texture.GetFileName();
		value["position"] = SerializeTransform( texture.GetPosition() );
		value["width"] = texture.GetWidth();
		value["height"] = texture.GetHeight();
		return value;
	}

	Body DeserializeBody( const Json::Value& value ) {
		Body body(
			value["mass"].asFloat(),
			value["inertia"].asFloat(),
			value["friction"].asFloat(),
			value["restitution"].asFloat() );

		Json::Value jsonCircles = value["circles"];
		for( size_t i = 0; i < jsonCircles.size(); ++i )
			body.AddCircle( DeserializeCircle( jsonCircles[i] ) );

		Json::Value jsonPolygons = value["polygons"];
		for( size_t i = 0; i < jsonPolygons.size(); ++i )
			body.AddPolygon( DeserializePolygonShape( jsonPolygons[i] ) );

		return body;
	}

	Json::Value SerializeBody( const Body& body ) {
		Json::Value jsonValue;
		jsonValue["mass"] = body.GetMass();
		jsonValue["inertia"] = body.GetInertia();
		jsonValue["friction"] = body.GetFriction();
		jsonValue["restitution"] = body.GetRestitution();

		Json::Value jsonCircles;
		for( IteratorPtr<Circle> circlesIter( body.GetCircles() ); !circlesIter.IsEnd(); circlesIter.Next() )
			jsonCircles.append( SerializeCircle( circlesIter.Current() ) );

		Json::Value jsonPolygons;
		for( IteratorPtr<PolygonShape> polygonsIter( body.GetPolygons() ); !polygonsIter.IsEnd(); polygonsIter.Next() )
			jsonPolygons.append( SerializePolygonShape( polygonsIter.Current() ) );

		jsonValue["circles"] = jsonCircles;
		jsonValue["polygons"] = jsonPolygons;

		return jsonValue;
	}

	Circle DeserializeCircle( const Json::Value& value ) {
		return Circle( DeserializeVec2( value["center"] ), value["radius"].asFloat() );
	}

	Json::Value SerializeCircle( const Circle& circle ) {
		Json::Value value;
		value["center"] = SerializeVec2( circle.Center );
		value["radius"] = circle.Radius;
		return value;
	}

	PolygonShape DeserializePolygonShape( const Json::Value& value ) {
		Transform position = DeserializeTransform( value["position"] );
		Polygon polygon = DeserializePolygon( value["vertices"] );

		return PolygonShape( position, polygon );
	}

	Json::Value SerializePolygonShape( const PolygonShape& dataPolygon ) {
		Json::Value value;
		value["position"] = SerializeTransform( dataPolygon.GetPosition() );
		value["vertices"] = SerializePolygon( dataPolygon.GetPolygon() );
		return value;
	}

	Polygon DeserializePolygon( const Json::Value& value ) {
		Polygon polygon;

		for( size_t i = 0; i < value.size(); ++i )
			polygon.AddVertex( DeserializeVec2( value[i] ) );

		return polygon;
	}

	Json::Value SerializePolygon( const Polygon& polygon ) {
		Json::Value jsonVertices;
		for( int i = 0; i < polygon.GetVerticesCount(); ++i )
			jsonVertices.append( SerializeVec2( polygon[i] ) );

		return jsonVertices;
	}

	Transform DeserializeTransform( const Json::Value& value ) {
		return Transform( DeserializeVec2( value["vector"] ), value["rotation"].asFloat() );
	}

	Json::Value SerializeTransform( const Transform& transform ) {
		Json::Value value;
		value["vector"] = SerializeVec2( transform.Vec );
		value["rotation"] = transform.Rot;
		return value;
	}

	Vec2 DeserializeVec2( const Json::Value& value ) {
		return Vec2( value["x"].asFloat(), value["y"].asFloat() );
	}

	Json::Value SerializeVec2( const Vec2& vec2 ) {
		Json::Value value;
		value["x"] = vec2.X;
		value["y"] = vec2.Y;
		return value;
	}
}
